// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------

namespace System.Fabric.Management.WindowsFabricValidator
{
    using System;
    using System.Collections.Generic;
    using System.Fabric.Management.ServiceModel;

    internal class HttpAppGatewaySettingsValidator
    {
        private Dictionary<string, WindowsFabricSettings.SettingsValue> httpAppGatewaySettings;
        private readonly ClusterManifestTypeNodeType[] nodeTypes;
        private List<InfrastructureNodeType> infrastructureInformation;
        private ClusterManifestType clusterManifest;
        private Dictionary<string, WindowsFabricSettings.SettingsValue> serviceCommonNameAndIssuerSettings;

        public bool IsHttpAppGatewayEnabled { get; private set; }

        public HttpAppGatewaySettingsValidator(
            ClusterManifestTypeNodeType[] nodeTypes,
            Dictionary<string, WindowsFabricSettings.SettingsValue> httpAppGatewaySettings,
            List<InfrastructureNodeType> infrastructureInformation,
            ClusterManifestType clusterManifest,
            Dictionary<string, WindowsFabricSettings.SettingsValue> serviceCommonNameAndIssuerSettings)
        {
            this.httpAppGatewaySettings = httpAppGatewaySettings;
            // Disabled by default
            this.IsHttpAppGatewayEnabled = false;
            this.nodeTypes = nodeTypes;
            this.infrastructureInformation = infrastructureInformation;
            this.clusterManifest = clusterManifest;
            this.serviceCommonNameAndIssuerSettings = serviceCommonNameAndIssuerSettings;
        }

        public void ValidateHttpGatewaySettings()
        {
            if (this.httpAppGatewaySettings != null &&
                this.httpAppGatewaySettings.ContainsKey(FabricValidatorConstants.ParameterNames.IsEnabled))
            {
                this.IsHttpAppGatewayEnabled = Boolean.Parse(this.httpAppGatewaySettings[FabricValidatorConstants.ParameterNames.IsEnabled].Value);
            }

            if (this.clusterManifest.Infrastructure.Item is ClusterManifestTypeInfrastructurePaaS)
            {
                // Reverseproxy/application gateway can be enabled in some nodetypes and not in others.
                // For PaaSV2 topology, the information of all node types is not available in the cluster manifest, so
                // we will not be able to validate the fact that when HttpAppGatewayEnabled is true, atleast one node type
                // has the reverseproxy/application gateway port specified. This is ok, because the manifest is generated by
                // the SFRP and not directly by users.
                return;
            }

            if (this.IsHttpAppGatewayEnabled)
            {
                bool isPortSpecified = false;
                if (infrastructureInformation == null)
                {
                    foreach (ClusterManifestTypeNodeType nodeType in this.nodeTypes)
                    {
                        if (nodeType.Endpoints != null && nodeType.Endpoints.HttpApplicationGatewayEndpoint != null)
                        {
                            ValidateEndpoint(nodeType.Endpoints.HttpApplicationGatewayEndpoint);
                            isPortSpecified = true;
                        }
                    }
                }
                else
                {
                    foreach (var node in this.infrastructureInformation)
                    {
                        if (node.Endpoints != null && node.Endpoints.HttpApplicationGatewayEndpoint != null)
                        {
                            ValidateEndpoint(node.Endpoints.HttpApplicationGatewayEndpoint);
                            isPortSpecified = true;
                        }
                    }
                }

                if (isPortSpecified == false)
                {
                    throw new ArgumentException(
                        "ValidateSFReverseProxySettings failed as no nodetype has the HttpApplicationGatewayListenAddress specified when reverseproxy is enabled");
                }
            }
        }

        private void ValidateEndpoint(InputEndpointType httpEndpoint)
        {
            if (httpEndpoint.Protocol != InputEndpointTypeProtocol.http
                && httpEndpoint.Protocol != InputEndpointTypeProtocol.https)
            {
                throw new ArgumentException(
                    string.Format(
                    "ValidateSFReverseProxySettings failed as HttpAppGatewayEndpoint.Protocol is invalid: {0}",
                    httpEndpoint.Protocol));
            }

            //
            // If protocol is https, then server certificates have to be specified.
            //
            if (httpEndpoint.Protocol == InputEndpointTypeProtocol.https)
            {
                if (!this.httpAppGatewaySettings.ContainsKey(FabricValidatorConstants.ParameterNames.HttpAppGatewayCredentialType) ||
                    this.httpAppGatewaySettings[FabricValidatorConstants.ParameterNames.HttpAppGatewayCredentialType].Value != "X509")
                {
                    throw new ArgumentException(
                        "ValidateSFReverseProxySettings failed as Credential type is not x509 when protocol is https");
                }

                if (!this.httpAppGatewaySettings.ContainsKey(FabricValidatorConstants.ParameterNames.HttpAppGatewayCertificateFindValue) ||
                    this.httpAppGatewaySettings[FabricValidatorConstants.ParameterNames.HttpAppGatewayCertificateFindValue].Value.Length == 0)
                {
                    throw new ArgumentException(
                        "ValidateSFReverseProxySettings failed as Certificate find value not set when protocol is https");
                }

                if (!this.httpAppGatewaySettings.ContainsKey(FabricValidatorConstants.ParameterNames.HttpAppGatewayCertificateValidationPolicy) ||
                    (this.httpAppGatewaySettings[FabricValidatorConstants.ParameterNames.HttpAppGatewayCertificateValidationPolicy].Value != FabricValidatorConstants.HttpAppGatewayPolicyNone &&
                    this.httpAppGatewaySettings[FabricValidatorConstants.ParameterNames.HttpAppGatewayCertificateValidationPolicy].Value != FabricValidatorConstants.ParameterNames.HttpAppGatewayServiceCertificateThumbprints &&
                    this.httpAppGatewaySettings[FabricValidatorConstants.ParameterNames.HttpAppGatewayCertificateValidationPolicy].Value != FabricValidatorConstants.HttpAppGatewayServiceCommonNameAndIssuer))
                {
                    throw new ArgumentException(
                        "ValidateSFReverseProxySettings failed as ApplicationCertificateValidationPolicy set incorrectly when protocol is https.");
                }

                if (this.httpAppGatewaySettings[FabricValidatorConstants.ParameterNames.HttpAppGatewayCertificateValidationPolicy].Value == FabricValidatorConstants.ParameterNames.HttpAppGatewayServiceCertificateThumbprints &&
                    (!this.httpAppGatewaySettings.ContainsKey(FabricValidatorConstants.ParameterNames.HttpAppGatewayServiceCertificateThumbprints) || String.IsNullOrEmpty(this.httpAppGatewaySettings[FabricValidatorConstants.ParameterNames.HttpAppGatewayServiceCertificateThumbprints].Value)))
                {
                    throw new ArgumentException(
                        "ValidateSFReverseProxySettings failed as parameter ServiceCertificateThumbprints not found when ApplicationCertificateValidationPolicy is set to thumbprints.");
                }

                if (this.httpAppGatewaySettings[FabricValidatorConstants.ParameterNames.HttpAppGatewayCertificateValidationPolicy].Value == FabricValidatorConstants.HttpAppGatewayServiceCommonNameAndIssuer &&
                    (this.serviceCommonNameAndIssuerSettings == null || this.serviceCommonNameAndIssuerSettings.Count <= 0))
                {
                    throw new ArgumentException(
                        "ValidateSFReverseProxySettings failed as section ServiceCommonNameAndIssuer not found or is empty when ApplicationCertificateValidationPolicy is set to ServiceCommonNameAndIssuer.");
                }
            }

            //
            // It is ok to have some nodetypes which have http endpoints while others have https endpoints for the app gateway.
            // This will be the configuration if the customer wants to expose only one nodetype via the load balancer and wants
            // to use the other nodetypes for internal communication.
            //
        }
    }
}